
#ifndef CMDBTHREADINTERFACE_H
#define CMDBTHREADINTERFACE_H
#define __MYSQL__

#include "RtDefines.h"
#include "RtError.h"
#include "RtDebug.h"
#include "RtUtilTemplates.h"
#include "RtReferenceControl.h"
#include "RtThreadInterface.h"
#include "dboci_adaptor.h"
#include "resource_center.h"
#include <vector>
#include <map>

class CDBHelper;
#ifdef __ORACLE__
///////////////////////////////////////////
//class CDBStoredProcedureHelper
///////////////////////////////////////////
class RT_API_EXPORT CDBStoredProcedureHelper
{
public:
	typedef
	enum 
	{ 
		PARAM_TYPE_IN = 0,
		PARAM_TYPE_OUT,
		PARAM_TYPE_IN_OUT
	}ParamType;
public:
	CDBStoredProcedureHelper();
	~CDBStoredProcedureHelper();

	RtResult PrepareProcedure(
		CRtString& strProcedure, CDBOciAdaptor* pDB);

	/*
	 *	When pType == PARAM_TYPE_OUT, dwParam is omitted
	 */
	RtResult BindParam(
		LPCSTR szParameter, 
		DWORD dwParam, ParamType pType);

	/*
	 *	When pType == PARAM_TYPE_OUT, strParam is omitted
	 */
	RtResult BindParam(
		LPCSTR szParameter, 
		LPCSTR szParam, ParamType pType);
	
	RtResult ExcuteProcedure();

	RtResult GetParamValue(LPCSTR szParameter, DWORD &dwVal);
	
	RtResult GetParamValue(LPCSTR szParameter, CRtString &strVal);
protected:
	void Clean();
protected:
	CDBOciAdaptor* m_pDB;

	std::map<CRtString, char*> m_mOutStrParams;
	std::map<CRtString, DWORD*> m_mOutIntParams;
	
	std::vector<char*> m_vstrInTmps;
	std::vector<DWORD*> m_vIntInTmps;
};
#endif

///////////////////////////////////////////
//class CDBHelper
///////////////////////////////////////////
class RT_API_EXPORT CDBHelper
{
public:
	CDBHelper();
	virtual ~CDBHelper();
public:
	/*
	 * bGetRecordSet : Need Recordset if doing SELECT
	 */
	RtResult DoSQL(const CRtString &strSQL, CDBOciAdaptor* pDB, BOOL bGetRecordSet);
	RtResult DoSQLAutoInc(const CRtString& strSQL, CDBOciAdaptor* pDB, LONGLONG& ulAutoValue);
	RtResult DoProcedure(CRtString &strProcedure, CDBOciAdaptor* pDB);

	/*
	 * Recordset methods
	 */
	WORD GetAffectRows(){return m_wAffectRows;}
	WORD GetRecordRows();	//How many rows have been selected
	WORD GetRecordCols();	//How many cols every row
	RtResult GetNextRow();	//Fetch next row of RecordSet, Return CM_OK if there has next row
	
	RtResult GetString(WORD wFieldIndex, CRtString &strVal);
	
	RtResult GetShort(WORD wFieldIndex, SHORT &sVal);
	RtResult GetLong(WORD wFieldIndex, LONG &lVal);
	
	RtResult GetUShort(WORD wFieldIndex, WORD &usVal);
	RtResult GetULong(WORD wFieldIndex, DWORD &ulVal);

	RtResult GetLongLong(WORD wFieldIndex, LONGLONG &llVal);

	RtResult GetReal(WORD wFieldIndex, double &rVal);
#ifdef __ORACLE__
	RtResult GetGMTTime(WORD wFieldIndex, gmt_time &tm);
#endif

public:
	static char* Number2String(DWORD dwNumber);
	/*
	 *	Example : szStr = "1234";
	 */
	static DWORD String2Number(const char* szStr);
	/*
	 *	Return string format : "YY-MM-DD HH:MI:SS"
	 */
	static char* GetCurrentDate();
	static DWORD g_dwExNum;
protected:
	void GetOCIValue(WORD wFieldIndex, void* &pvVal);
protected:
	CDBOciAdaptor* m_pDB;
	CRecordset_* m_pRecordSet;
	WORD m_wCols;
	WORD m_wRows;
	WORD m_wRowIndex;
	WORD m_wAffectRows;

	char m_ch[2];	//For SQLT_AFC case
};

#ifdef __ORACLE__
///////////////////////////////////////////
//class CDBParaBindSQLHelper
///////////////////////////////////////////
class RT_API_EXPORT CDBParaBindSQLHelper : public CDBHelper
{
public:
	CDBParaBindSQLHelper();
	~CDBParaBindSQLHelper();

	/*
	 * bGetRecordSet : Need Recordset if doing SELECT
	 */
	RtResult PrepareSQL(
		CRtString& strSQL, CDBOciAdaptor* pDB, BOOL bGetRecordSet);

	RtResult BindParam(
		LPCSTR szParameter, DWORD dwParam);

	RtResult BindParam(
		LPCSTR szParameter, LPCSTR szParam);
	
	RtResult ExcuteSQL();
protected:
	void Clean();
	RtResult StrReplace(CRtString& strSQL, CRtString strParam, CRtString strVal);
protected:
	CDBOciAdaptor* m_pDB;

	std::vector<char*> m_vstrInTmps;
	std::vector<DWORD*> m_vIntInTmps;

	BOOL m_bGetRecordSet;
	CRtString m_strSQLSelect;
};
#endif
///////////////////////////////////////////
//class IDBEvent
///////////////////////////////////////////
class RT_API_EXPORT IDBEvent : public IRtReferenceControl
{
public:
	virtual void SetDB(HANDLE hDB) = 0;

	virtual RtResult OnRequest() = 0;
	virtual BOOL NeedResponse() = 0;
	virtual RtResult OnResponse() = 0;
};

///////////////////////////////////////////
//class CDBEventBase
///////////////////////////////////////////
class RT_API_EXPORT CDBEventBase : 
	public IDBEvent, 
	public CRtReferenceControlMutilThread
{
public:
	CDBEventBase() { m_pDB = NULL; }
	virtual ~CDBEventBase() {}
public:
	virtual DWORD AddReference()
	{
		return CRtReferenceControlMutilThread::AddReference();
	}
	virtual DWORD ReleaseReference()
	{
		return CRtReferenceControlMutilThread::ReleaseReference();
	}
public:	
	virtual void SetDB(HANDLE hDB) { m_pDB = (CDBOciAdaptor*)hDB; }

	/*
	 * The following functions should be inherited by user
	 */
	virtual RtResult OnRequest() = 0;
	virtual BOOL NeedResponse() { return TRUE; } // Just return TRUE or FALSE
	virtual RtResult OnResponse() { return RT_OK; }
protected:
	CDBOciAdaptor* m_pDB;
};

///////////////////////////////////////////
//class CRcDbEventBase
///////////////////////////////////////////
class RT_API_EXPORT CRcDbEventBase : 
	public IDBEvent, 
	public CRtReferenceControlMutilThread
{
public:
	CRcDbEventBase() { m_hDB = NULL; }
	virtual ~CRcDbEventBase() {}
public:
	virtual DWORD AddReference()
	{
		return CRtReferenceControlMutilThread::AddReference();
	}
	virtual DWORD ReleaseReference()
	{
		return CRtReferenceControlMutilThread::ReleaseReference();
	}
public:	
	virtual void SetDB(HANDLE hDB) { m_hDB = hDB; }

	/*
	 * The following functions should be inherited by user
	 */
	virtual RtResult OnRequest() = 0;
	virtual BOOL NeedResponse() { return TRUE; } // Just return TRUE or FALSE
	virtual RtResult OnResponse() { return RT_OK; }
protected:
	HANDLE m_hDB;
};

///////////////////////////////////////////
//class CDBThreadManager
///////////////////////////////////////////
class RT_API_EXPORT CDBThreadManager
: public CRtReferenceControlSingleThread
{
// 	friend class CRtSingletonT<CDBThreadManager>;
// public:
// 	static CDBThreadManager* Instance();	
public:	
	CDBThreadManager();
	virtual ~CDBThreadManager();
	enum{
		DB_TYPE_MYSQL	= 0,
		DB_TYPE_PGSQL	= 1,
		DB_TYPE_MEMCACHE= 2,
		DB_TYPE_MGDB	= 3
	};
	/*
	 * wThreadNum : How many DB threads do you need
	 */
	RtResult Initialize(LPCSTR szDBSvr,
		LPCSTR szDBName,
		LPCSTR szUserName,
		LPCSTR szPasswd,
		WORD wThreadNum,
		BOOL bThreadAutoSelect,
		WORD wDBType = DB_TYPE_MYSQL);

	/*
	 * wThreadNum : How many DB threads do you need
	 */
	RtResult Initialize2(LPCSTR szDBSvr,
		unsigned short nPort,
		LPCSTR szDBName,
		LPCSTR szUserName,
		LPCSTR szPasswd,
		WORD wThreadNum,
		BOOL bThreadAutoSelect,
		WORD wDBType = DB_TYPE_MYSQL);

	BOOL IsDBConnected() const
	{
		if(m_vDBAdaptors[m_wCurrentIndex] && DB_TYPE_MYSQL == m_wDBType)
		{
			CDBOciAdaptor* pAda = (CDBOciAdaptor*)m_vDBAdaptors[m_wCurrentIndex];
			return pAda->IsConnected();
		}
		return FALSE;
	}

	/*
	 * wThreadNo : Index of DB thread you want to PostEvent to
	 *             Index = (1, 2, 3, ...); Begin from 1
	 * 
	 * pDBEvent  : Will be ReleaseReference() when finish job.
	 *             If upper layer don't need to control its life time,  
	 *             just 'new' it without AddReference() by hand or by AutoPtr 
	 */
	RtResult PostEvent(IDBEvent* pDBEvent, WORD wThreadNo);

	/*
	 * Should call this function if Initialize(...) with bThreadAutoSelect = TRUE
	 */
	RtResult PostEvent(IDBEvent* pDBEvent);

	WORD GetThreadNum();
protected:
	void Clean();
protected:
	IRtEventQueue*	m_pCurrEventQueue;
	std::vector<CRtThread*> m_vDBThreads;
	std::vector<HANDLE> m_vDBAdaptors;
	//std::vector<HANDLE>		m_vRcDbAdaptors;
	WORD m_wThreadNum;
	BOOL m_bThreadAutoSelect;
	WORD m_wCurrentIndex;
	WORD m_nPushNumber;
	WORD m_wDBType;
};

#endif	//CMDBTHREADINTERFACE_H
